using System;
using System.Collections;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;

using Microsoft.XLANGs.BaseTypes;
using Microsoft.XLANGs.Core;

namespace TKH.BizTalk.Diagnostics
{
    /// <summary>
    /// The diagnostic view of an XLANG Message.
    /// Captures, Message context, message parts and thier part context.
    /// Uses System.Reflection to access the XMessage class in order to enumerate the context properties
    /// </summary>
    internal class Message
    {
        private string messageType = string.Empty;        
        private XmlDocument message;

        internal string MessageType
        {
            get { return messageType; }
        }
        
        internal Message(XLANGMessage xlangMessage)
        {
            messageType = Message.GetProperty(xlangMessage, (typeof(BTS.MessageType)));
            message = new XmlDocument();
            message.LoadXml("<Message/>");
            XmlNode contextNode = message.CreateElement("Context");
            message.DocumentElement.AppendChild(contextNode);
            
            //Message Context
            XMessage unwrappedMessage = (XMessage)typeof(MessageWrapperForUserCode).InvokeMember("Unwrap", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, xlangMessage, new object[0]);
            Hashtable contextHashtable = unwrappedMessage.GetContextProperties();
            if (contextHashtable != null)
            {
                foreach (DictionaryEntry entry in contextHashtable)
                {
                    XmlQName propertyName = (XmlQName)entry.Key;
                    string value = entry.Value.ToString();
                    XmlNode propertyNode = message.CreateElement("Property");
                    ((XmlElement)propertyNode).SetAttribute("name", propertyName.Name);
                    ((XmlElement)propertyNode).SetAttribute("namespace", propertyName.Namespace);
                    ((XmlElement)propertyNode).SetAttribute("value", value);
                    contextNode.AppendChild(propertyNode);
                }
            }
            message.DocumentElement.AppendChild(contextNode);

            //Message Parts
            XmlNode partsNode = message.CreateElement("Parts");
            
            foreach (XLANGPart part in xlangMessage)
            {
                string contentType = (string)part.GetPartProperty(typeof(ContentType));
                XmlNode partNode = message.CreateElement("Part");
                ((XmlElement)partNode).SetAttribute("name", part.Name);

                //Part Properties
                Part unwrappedPart = (Part)typeof(PartWrapperForUserCode).InvokeMember("Unwrap", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, part, new object[0]);
                
                XmlQNameTable partContext = unwrappedPart.GetPartProperties();
                if (partContext != null)
                {
                    XmlNode partPropertiesNode = message.CreateElement("Properties");
                    foreach (DictionaryEntry entry in partContext)
                    {
                        XmlQName propertyName = (XmlQName)entry.Key;
                        string value = entry.Value.ToString();
                        XmlNode propertyNode = message.CreateElement("Property");
                        ((XmlElement)propertyNode).SetAttribute("name", propertyName.Name);
                        ((XmlElement)propertyNode).SetAttribute("namespace", propertyName.Namespace);
                        ((XmlElement)propertyNode).SetAttribute("value", value);
                        partPropertiesNode.AppendChild(propertyNode);
                    }
                    partNode.AppendChild(partPropertiesNode);
                }
                partsNode.AppendChild(partNode);
                XmlNode contentNode = message.CreateElement("Content");
                Type partType = unwrappedPart.GetPartType();
                ((XmlElement)partNode).SetAttribute("type", partType.ToString());
                try
                {
                    XmlDocument temp = (XmlDocument)part.RetrieveAs(typeof(XmlDocument));
                    XmlNode node = message.ImportNode(temp.DocumentElement, true);
                    contentNode.AppendChild(node);
                    partNode.AppendChild(contentNode);
                }
                catch{}             
            }
            message.DocumentElement.AppendChild(partsNode);
        }

        public XPathNavigator CreateNavigator()
        {
            return message.CreateNavigator();
        }

        public override string ToString()
        {
            return string.Format("XLANG Message\nMessage Type:\t{0}\n{1}", messageType, message.OuterXml);
        }

        internal static string GetProperty(XLANGMessage message, Type propertyType)
        {
            string result = string.Empty;
            try
            {
                MessageWrapperForUserCode wrapper = message as MessageWrapperForUserCode;
                object propertyValue = wrapper.GetPropertyValue(propertyType);
                if (propertyValue != null)
                {
                    result = propertyValue.ToString();
                }
            }
            catch{}
            return result;
        }
    }
}
